from debug import *
from buffer import *
from resource import buffer_list
import copy

def Fifo_gready(tasks,resource):
    sys_time = 0 #系统时间
    hit_count = 0 #统计命中次数
    dirty_count = 0

    resource_run_time = copy.copy(resource) #拷贝一个出来用于记录运行时间

    resource_utilization = [] #记录每个时刻的有效忙碌资源数
    sampling_interval = 100
    last_sampling_time = -1

    #贪心方法，按顺序遍历任务集合，有空闲资源即分配给下一任务
    finish = False
    while not finish:
        #每隔100us采样一次
        if sys_time >= last_sampling_time+sampling_interval:
            last_sampling_time = sys_time
            busy_count = 0
            for unit in resource:
                busy_count += resource[unit]
            resource_utilization.append(busy_count)

        should_increse_time_stage = [] #不应该记录时间的id

        #遍历每个任务,进行当前时间下的状态转换
        for task in tasks.task_list:
            #如果当前任务已完成则直接跳过
            if(task.task_done):
                continue

            #查找已完成的任务
            for running_task in task.running_task[:]:
                if(running_task[1] <= sys_time): #如果系统已经到达了该任务的完成时间
                    #如果是nfc任务的话，看看arm是不是在忙；如果在忙的话就不能驱逐这个任务，等到下一个时间片再看
                    if "nfc" in running_task[2]:
                        if resource["arm"] != 0:
                            #不应该将这个节点的时间参与下一节点时间计算，否则这个节点的时间一直会被作为下一状态时间
                            should_increse_time_stage.append(running_task)
                            #如果有一个是因为arm被阻塞了那么不算入有效资源利用率时间
                            resource_utilization.append(resource_utilization.pop()-1)
                            continue
                    
                    #计算资源运行时间
                    resource_run_time[running_task[2]] += running_task[1]-running_task[3]

                    task.finish_task.append(running_task[0])
                    resource[running_task[2]] = 0 #释放相关运行资源
                    if is_print:
                        print(sys_time,"us task:",task.task_name,running_task[0],"step finished")
                    #从运行任务中移除该任务
                    task.running_task.remove(running_task)

            #判断是否完成了当前任务的所有任务
            if(len(task.finish_task)-1 == task.task_count): #-1的原因是有个初始任务0导致finish_task会大1
                if is_print:
                    print(sys_time,"us task:",task.task_name,"done")
                task.task_done = True
                #写入完成时间
                task.end_time = sys_time

        #查找可执行的任务
        for task in tasks.task_list:
            #如果当前任务已完成则直接跳过
            if(task.task_done):
                continue

            for operation in task.task:
                #如果当前操作已完成则跳过
                if operation[0] in task.finish_task:
                    continue
                #或者在执行中也跳过
                running = False
                for running_task in task.running_task:
                    if operation[0] == running_task[0]:
                        running = True
                        break
                if running:
                    continue

                #未启动的任务，检查前置任务是否完成
                front_finish = True
                for front in operation[3]:
                    if not(front in task.finish_task): #如果有前置任务没有完成，则当前任务不能启动
                        front_finish = False 

                #这个任务的前置任务都完成了
                if front_finish:
                    #检查所需资源
                    if operation[1] in resource: #如果所需资源完整名称匹配成功，说明对资源的需求是特定的
                        if resource[operation[1]] == 0: #该资源没有被占用
                            # 如果是nfc任务的话,需要检查是否命中
                            # 如果命中的话执行时间为0
                            # 如果没有命中的话检查驱逐出来的buffer项是否为脏，为脏的话执行时间需要加上写回脏页的时间，不为脏的话执行时间
                            operation_time = operation[2]
                            if "nfc" in operation[1]:
                                channel = operation[4][0]
                                buffer_instance = buffer_list[channel-1]
                                page = operation[4][1]
                                if(buffer_instance.is_hit(page)):
                                    hit_count += 1
                                    operation_time = 0
                                else:
                                    #如果没有命中，我们需要分配一个buffer
                                    ret = buffer_instance.allocate(page)
                                    if ret == False: #完了嘛，驱逐了一个脏页出来
                                        dirty_count += 1
                                        operation_time +=  600

                                if "写" in task.task_name or "Write" in task.task_name:
                                    buffer_instance.mark_dirty(page)

                            # # 如果是host操作的话，如果是一个写操作那么需要置脏页
                            # if "host" in operation[1] and "写" in task.task_name:
                            #     channel = operation[5]-1
                            #     buffer_instance = buffer_list[channel]
                            #     page = operation[4]
                            #     buffer_instance.mark_dirty(page)

                            #将任务加入运行队列
                            task.running_task.append([operation[0],sys_time+operation_time,operation[1],sys_time]) #将开始时间加入到里面好统计每个器件的执行时间
                            #占用相应资源
                            resource[operation[1]] = 1
                            #分配buffer

                            if is_print:
                                print(sys_time,"us task:",task.task_name,operation[0],"start in",operation[1])

                    else: #如果所需资源不在里面，说明可能是不固定的分配
                        for unit in resource: #遍历查找资源名称，查看是否有部分匹配成功的
                            if operation[1] in unit: #如果部分匹配成功，说明这是一个对资源要求不固定的，可以分配到任意一个资源上
                                if resource[unit] == 0: #如果这个可分配的资源可用
                                    #将任务加入运行队列
                                    task.running_task.append([operation[0],sys_time+operation[2],unit,sys_time])
                                    #占用相应资源
                                    resource[unit] = 1
                                    if is_print:
                                        print(sys_time,"us task:",task.task_name,operation[0],"start in",unit)
                                    break #这个任务分配完成，不用再找了
                    

        #当前系统时间下的状态转移完成，将系统时间设置为下一个即将完成的任务的时间
        MAX_TIME = 0xFFFFFFFFFFFFFFFF
        temp_time = MAX_TIME
        for task in tasks.task_list:
            #如果当前任务已完成则直接跳过
            if(task.task_done):
                continue
            
            for running_task in task.running_task:
                #如果是需要延长时间的stage，那么跳过不算
                if running_task in should_increse_time_stage:
                    continue
                if(running_task[1] < temp_time):
                    temp_time = running_task[1]

            if(temp_time != MAX_TIME):
                sys_time = temp_time

        #检查任务是否全部完成
        finish = True
        for task in tasks.task_list:
            if(task.task_done == False):
                finish = False
                break

    #统计平均时延
    total_latency = 0
    for task in tasks.task_list:
        total_latency += task.end_time
    avg_latency=round(total_latency/float(tasks.task_count),2)

    if is_print:
        print("total time:",sys_time,"us")
        print("average latency:",avg_latency,"us")

    #统计并行度
    avg_busy_count = sum(resource_utilization)/len(resource_utilization)
    ratio = round(avg_busy_count/len(resource),3) * 100

    #统计资源利用率（每个资源的有效运行时间/makespan 然后取平均值）
    unit_runtime_ratio = []
    for unit_time in resource_run_time:
        unit_runtime_ratio.append(round(resource_run_time[unit_time]/sys_time,3)*100)
    avg_runtime_ratio = round(sum(unit_runtime_ratio)/len(unit_runtime_ratio),1)

    return sys_time,avg_latency,hit_count,dirty_count,avg_runtime_ratio